package com.newtouch.cq.controllerlog.aspect;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.newtouch.cq.controllerlog.business.entity.SysLog;
import com.newtouch.cq.controllerlog.business.service.ISysLogService;
import com.newtouch.cq.controllerlog.config.LogProperties;
import com.newtouch.cq.lang.em.BaseExceptionEnum;
import com.newtouch.cq.lang.exception.IMccExceptionInfo;
import com.newtouch.cq.lang.util.ServletUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class ControllerLogAspect implements Serializable {
    private static final long serialVersionUID = 548449245657027165L;

    private Log log = LogFactory.get(ControllerLogAspect.class);

    @Autowired
    private ISysLogService logService;

    private static final String pointcut = "(within(@org.springframework.stereotype.Controller *)"
            + " or within(@org.springframework.web.bind.annotation.RestController *))"
            + " and !within(@com.newtouch.cq.lang.annotation.NoLog *)"
            + " and !@annotation(com.newtouch.cq.lang.annotation.NoLog)"
            + " and !within(@springfox.documentation.annotations.ApiIgnore *)"
            + " and !@annotation(springfox.documentation.annotations.ApiIgnore)";
    @Around(pointcut)
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        Timestamp end = null;
        HttpServletRequest request = null;
        Exception aroundException = null;
        LocalDateTime startTime = LocalDateTimeUtil.now();
        try {
            result = joinPoint.proceed();
        } catch (Exception e) {
            aroundException = e;
        }
        String code = "";
        String message = "";
        if (ObjectUtil.isNotNull(aroundException)) {
            if (aroundException instanceof IMccExceptionInfo) {
                code = ((IMccExceptionInfo<String>) aroundException).getCode();
                message = ((IMccExceptionInfo) aroundException).getMessage();
            } else if ((StrUtil.contains(aroundException.getClass().getName(), "AccessDeniedException"))) {
                code = BaseExceptionEnum.INSUFFICIENT_PERMISSIONS.getCode();
                message = BaseExceptionEnum.INSUFFICIENT_PERMISSIONS.getMessage();
            } else {
                code = BaseExceptionEnum.ERROR.getCode();
                message = BaseExceptionEnum.ERROR.getMessage();
            }
            saveLog(startTime, joinPoint, code, message);

            throw aroundException;
        } else {
            try {
                JSONObject jsonObject = JSONUtil.parseObj(result);
                code = jsonObject.getStr("code");
                message = jsonObject.getStr("message");
            } catch (Exception e) {
                log.error("切片获取响应码异常，响应结果:{}", JSONUtil.toJsonStr(result));
            }
            saveLog(startTime, joinPoint, code, message);
        }


        return result;
    }

    private void saveLog(LocalDateTime startTime, ProceedingJoinPoint joinPoint, String code, String message) {
        if(LogProperties.disableLog) {
           return;
        }
        try {
            HttpServletRequest request = ServletUtils.getRequest();
            String uri = request.getRequestURI();
            String method = request.getMethod();
            String requestIp = request.getRemoteAddr();
            String execMethod = joinPoint.getSignature().getDeclaringTypeName() + "."
                    + joinPoint.getSignature().getName();
            String params = JSONUtil.toJsonStr(getRequestParam(joinPoint));
            log.info("请求开始-地址-{}-时间-{}-ip-{}-方法名-{}-参数-{}--响应编码--{}", uri, startTime.toString(), requestIp, execMethod, params);
            LocalDateTime endTime = LocalDateTimeUtil.now();
            JSONObject operator = operator();
            Long operatorId = null;
            String operatorName = null;
            if (!JSONUtil.isNull(operator)) {
                operatorId = operator.getLong("id");
                operatorName = StrUtil.isNotEmpty(operator.getStr("username")) ?
                        operator.getStr("username") : operator.getStr("telephone");
            }

            SysLog sysLog = new SysLog();
            sysLog.setRequestIp(requestIp);
            sysLog.setParams(params);
            sysLog.setUri(uri);
            sysLog.setMethod(method);
            sysLog.setCode(code);
            sysLog.setMessage(message);
            sysLog.setOperateTime(startTime);
            sysLog.setTimeTake(LocalDateTimeUtil.between(startTime, endTime, ChronoUnit.MILLIS));
            sysLog.setCreateId(operatorId);
            sysLog.setCreateName(operatorName);
            sysLog.setCreateTime(endTime);
            logService.save(sysLog);
        } catch (Exception e) {

        }
    }

    private JSONObject operator() {
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return JSONUtil.parseObj(principal);
    }


    public Map<String, Object> getRequestParam(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        DefaultParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = pnd.getParameterNames(method);
        Map<String, Object> paramMap = MapUtil.newHashMap();
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
        }
        return paramMap;
    }
}
